#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>

#include <pthread.h>

#include "simplednsd.h"

#define DB_SIZE 20
typedef struct {
	ns_req request;
	int rsk;
	struct sockaddr_in remote;
	pthread_t thread;
} t_req;

ns_req records[DB_SIZE];

int records_size = 0;

struct sockaddr_in local;
int sk;

void interrupt(int sig)
{
	printf("shutting down...\n");
	//db->close(db, 0);
	exit(sig);
}

void answer_query(ns_req * req, ns_req * answer)
{
	int i;
	for(i = 0; i < records_size; i++)
	{
		if(strcmp(req->host_name, records[i].host_name) == 0)
		{
			memcpy(answer, &records[i], sizeof(ns_req));
			answer->type = 'A';
			return;
		}
	}
	answer->type='N';
}

void insert_record(ns_req * req)
{
	printf("inserting record...\n");
	long int addr;
	unsigned short port;
	memcpy(&addr, req->addr, 4);
	memcpy(&port, req->port, 2);
	printf("type: %c\n", req->type);
	printf("name: %s\n", req->host_name);
	printf("addr: %x\n", addr);
	printf("port: %d\n", port);
	memcpy(&records[records_size++], req, sizeof(ns_req));
}

void * handle_request(void * buffer)
{
	t_req * tr = (t_req *) buffer;
	ns_req answer;
	ns_req * req = &tr->request;
	long int addr;
	unsigned short port;
	memcpy(&addr, req->addr, 4);
	memcpy(&port, req->port, 2);
	switch (req->type) {
		case 'Q':
			answer_query(req, &answer);
			printf("writing back response...\n");
			long int ans_addr = 0;
			short ans_port = 0;
			memcpy(&ans_addr, answer.addr, 4);
			memcpy(&ans_port, answer.port, 2);
			printf("name: %s\n", answer.host_name);
			printf("addr: %x\n", ans_addr);
			printf("port: %d\n", ans_port);
			//sendto(sk, &answer, sizeof(ns_req), 0, (struct sockaddr *)&tr->remote, sizeof(struct sockaddr));
			send(tr->rsk, &answer, sizeof(ns_req), 0);
			break;
		default:
			insert_record(req);
			//sendto(sk, req, sizeof(ns_req), 0, (struct sockaddr *)&tr->remote, sizeof(struct sockaddr));
			send(tr->rsk, req, sizeof(ns_req), 0);
			break;
	}
	free(buffer);
	pthread_exit(NULL);
}

int main(int argc, char **argv) {

	char local_host_name[128];
	char root_file_path[128];
	int port;

	signal(SIGINT, &interrupt);
	sk=socket(AF_INET,SOCK_STREAM,0);

	gethostname(local_host_name, 128);

	if(argc > 1)
	{
		strcpy(root_file_path, argv[1]);
	}
	else
	{
		strcpy(root_file_path, "/afs/cs.pitt.edu/usr0/agc7/public/nsroot");
	}

	if(argc > 2)
	{
		port = atoi(argv[2]);
	}
	else
	{
		port = 5353;
	}

	if(argc > 3)
	{
		strcpy(local_host_name, argv[3]);
	}
	/*Construct and bind the name using default values*/
	local.sin_family=AF_INET;
	local.sin_addr.s_addr=INADDR_ANY;
	local.sin_port=port;
	bind(sk,(struct sockaddr *)&local,sizeof(local));
	printf("Socket has port %d on %s\n",local.sin_port, local_host_name);

	FILE * fp = fopen(root_file_path, "a");
	fprintf(fp, "%s\n%d\n", local_host_name, local.sin_port);
	fclose(fp);
	printf("Listening...\n");
	listen(sk, 5);
	//fd_set ready_set;
	while(1) {
		//FD_ZERO(&ready_set);
		//FD_SET(sk, &ready_set);
		//if(select(sk+1, &ready_set, NULL, NULL, NULL) < 1)
		//	continue;
		//if(!FD_ISSET(sk, &ready_set))
		//	continue;
		int rsk = accept(sk,0,0);
		printf("got connection\n");
		t_req *tr = (t_req *) malloc(sizeof(t_req));
		tr->rsk = rsk;
		ns_req * req = &tr->request;
		bzero(req, sizeof(ns_req));
		//unsigned int rlen = sizeof(struct sockaddr);
		//recvfrom(sk, req, sizeof(ns_req), 0, (struct sockaddr *)&tr->remote, &rlen);
		recv(rsk, req, sizeof(ns_req), 0);
		printf("Got request\n");

		pthread_t * thread = (pthread_t *) &tr->thread;
		if(pthread_create(thread, NULL, handle_request, (void *) tr) != 0)
			fprintf(stderr, "An error occurred creating the thread");
	}
	return 0;
}
